%include "lib/global.inc"
%include "lib/portio.inc"
;定义一个缓冲区，保存一行的信息
section .data
buffer times 160 db 0

[bits 32]
section .text
;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
;name : LinePos2AbsPos/AbsPos2LinePos
;function : 线性坐标值/行列坐标值互相转化
;input / output :
;       eax -- 线性坐标
;       ebx -- 行列坐标
LinePos2AbsPos:
    push eax
    mov bl, 80
    div bl
    mov bl, ah
    mov bh, al
    pop eax
    ret
AbsPos2LinePos:
    push ebx
    mov ah, 80
    mov al, bh
    mul ah
    xor bh, bh
    add ax, bx
    pop ebx
    ret 
;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
;name : GetCursorPos/SetcursorPos
;function : 获得光标坐标/设置光标坐标
;output/input:
;       eax -- 线性值地址（每个值对应一个字符位置，2个字节）
;-------------------------------------------------------------------------------
;光标操作，地址寄存器通路0x03D4, 数据寄存器通路0x03D5
;光标高8位0x0e, 光标低8位0x0f
GetCursorPos:
    OUTB 0x03D4, 0x0E
    INB 0x03D5
    mov ah, al
    OUTB 0x03D4, 0x0F
    INB 0x03D5
    ;现在ah和al的值是线性值
    ret

; 同一个函数的_cdecl版本，用于C语言调用
global GetCursorPos_S
GetCursorPos_S:
    push ebx
    push esi
    push edi
    xor eax, eax
    call GetCursorPos
    pop edi
    pop esi
    pop ebx
    ret

;-------------------------------------------------------------------------------
;name : SetCursorPos
;function : 设置光标位置
;input :
;   栈传参 --  目标光标的线性地址
;output :
;   屏幕上的光标位置改变

;-------------------------------------------------------------------------------
global SetCursorPos
SetCursorPos:
    push ebx
    push esi
    push edi
    mov ebx, [esp + 16]
    OUTB 0x03D4, 0x0E
    OUTB 0x03D5, bh
    OUTB 0x03D4, 0x0F
    OUTB 0x03D5, bl
    pop edi
    pop esi
    pop ebx
    ret
;-------------------------------------------------------------------------------
;name : ScollScreen
;function : 在必要的时候滚屏
;input :   
;           bx --当前的坐标，如果bh不为25， 则直接退出
;output :
;           bh = 24, bl = 0 为最后一行的首坐标，行列表示
ScollScreen:
    pushad
    cld  
    mov ecx, 960				  ; 一共有2000-80=1920个字符要搬运,共1920*2=3840字节.一次搬4字节,共3840/4=960次 
    mov esi, 0xc00b80a0			  ; 第1行行首
    mov edi, 0xc00b8000			  ; 第0行行首
    rep movsd
    ;;;;;;;将最后一行填充为空白
    mov ebx, 3840			  ; 最后一行首字符的第一个字节偏移= 1920 * 2
    mov ecx, 80				  ;一行是80字符(160字节),每次清理1字符(2字节),一行需要移动80次
 .cls:
    mov word [gs:ebx], 0x0720		  ;0x0720是黑底白字的空格键
    add ebx, 2
    loop .cls 
    mov eax, 1920
    push eax
    call SetCursorPos
    add esp, 4
    call GetCursorPos
.Return:
    popad
    ret
;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
;name : putchar_S
;function : 输出单个字符, _cdecl
;input :
;       栈传参[ebp + 36] —— 待输出字符
;output :
;       屏幕输出
;-------------------------------------------------------------------------------
global putchar_S
putchar_S:
    pushad
    mov ebp, esp
    mov ax, SELECTOR_VIDEO
    mov gs, ax

    ;待输出字符
    mov ecx, [ebp + 36]
    ;获得坐标位置
    xor eax, eax
    call GetCursorPos
    call LinePos2AbsPos
    cmp ecx, 0x08       ;BackSpace
    je .BackSpace
    cmp ecx, 0x0D       ;Carriage Return
    je .CarriageReturn
    cmp ecx, 0x0A       ;Line Feed
    je .LineFeed
    jmp .OtherChar
.BackSpace:
    mov byte [gs:eax * 2], 0x20
    test bl, bl
    jnz .BackWhenNotZero
.BackWhenZero:
    mov bl, 80
    dec bh
.BackWhenNotZero:
    dec ebx
    call AbsPos2LinePos
    push eax
    call SetCursorPos
    jmp .Return

.LineFeed:
    inc bh
    cmp bh, 25
    jne .CarriageReturn
    call ScollScreen
    mov bh, 24
.CarriageReturn:
; 因为这里没有考虑上一个call可能会改变ebx的值，导致了重大的bug, 所以上面直接结束了
    mov bl, 0
    call AbsPos2LinePos
    push eax
    call SetCursorPos
    add esp, 4
    jmp .Return

.OtherChar:
    mov byte [gs : eax * 2], cl
    inc bl
    cmp bl, 80
    jne .SameLine
    inc bh
    xor bl, bl
    cmp bh, 25
    jne .SameLine
    call ScollScreen
    jmp .Return

.SameLine:
    call AbsPos2LinePos
    push eax
    call SetCursorPos
    add esp, 4
    jmp .Return


.Return:
    popad
    ret
;-------------------------------------------------------------------------------
;name : PutString_S
;function : 输出一个字符串，以\0为结束标记
;input :
;       ebp + 8 : 字符串的地址
;output :
;       void
;-------------------------------------------------------------------------------
global PutString_S
PutString_S:
    push ebp
    mov ebp, esp
    mov eax, [ebp + 8]
    push ebx
.EveryChar:
    cmp byte [eax], 0
    je .END
    push eax
    xor edx, edx
    mov dl, [eax]
    push edx
    call putchar_S
    add esp, 4     ;手动退掉参数
    pop eax
    inc eax
    jmp .EveryChar
.END:
    pop ebx
    pop ebp
    ret